package com.gatewayservice.gatewayservice.config;

import com.commonmodule.commonmodule.util.AESUtils;
import com.commonmodule.commonmodule.util.RedisUtil;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import jakarta.annotation.Resource;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 配置类   注册器
 * @author zxb
 * @version 1.0
 * @date 2024-10-24 10:30
 */
@Component
public class WebConfig implements GlobalFilter, Ordered {

    @Resource
    public StringRedisTemplate stringRedisTemplate;

    private static final String HEADER_NAME = "Acess-Token";

    // 加密密钥常量
    private final static String ENCRYPT_KEY = "xiangyu_test:";
    // 过期时间   秒
    private final static Integer EXPIRE_TIME = 1200;

    // 白名单
    private List<String> whiteList;
    public WebConfig() {
        ArrayList<String> whiteList = new ArrayList<>();
        // 账号密码登录
        whiteList.add("/auth/login");
        // 发送验证码
        whiteList.add("/auth/note");
        // 注册
        whiteList.add("/auth/enroll");
        // 验证码登录
        whiteList.add("/auth/authCodeLogin");
        this.whiteList = whiteList;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        System.out.println("==============拦截器==============");

        // 获取请求对象
        ServerHttpRequest request = exchange.getRequest();
        // 获取响应对象
        ServerHttpResponse response = exchange.getResponse();
        // 获取请求地址
        String url = request.getURI().getPath();
        // 获取token信息
        String token = request.getHeaders().getFirst(HEADER_NAME);

        // 判断是否为白名单请求 或不需要验证的请求
        // 如果当前请求中包含token令牌不为空的时候，也会继续验证Token的合法性，这样就能保证
        // Token中的用户信息被业务接口正常访问到了。而如果当token为空的时候，白名单的接口可以
        // 被网关直接转发，无需登录验证。当然被转发的接口，也无法获取到用户的token身份数据了
        if (whiteList.contains(url)) {
            return chain.filter(exchange);
        }

        // 校验token
        if (!verifyToken(token)) {
            // 设置错误状态码为 401
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            // 设置返回的信息为JSON类型
            exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
            // 自定义错误信息
            String errorMsg = "{\"error\": \"" + "用户未登录或登录超时，请重新登录" + "\"}";
            return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(errorMsg.getBytes())));
        }

        // 把新的 exchange 放回到过滤链
        ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, token).build();
        ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
        return chain.filter(newExchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }

    /**
     * 校验token
     * @param token
     * @return
     */
    // 校验token
    public boolean verifyToken(String token) {
        try {

            // token解密
            String data = AESUtils.getDecryptString(token);
            // 获取数据
            JsonElement jsonElement = JsonParser.parseString(data);
            JsonObject obj = jsonElement.getAsJsonObject();
            String token1 = obj.get("token").getAsString();

            String key = ENCRYPT_KEY + token1;

            // 校验token是否存在
            if (stringRedisTemplate.hasKey(key)) {
                // token过期时间
                String expireTime = obj.get("expireTime").getAsString();
                long l = Long.parseLong(expireTime);
                // 当前时间戳
                long l1 = System.currentTimeMillis();

                // 如果过期时间小于 30000毫秒   则刷新过期时间
                if (l > l1 && (l - l1) < 30000L) {
                    long newExpireTime = (long) (EXPIRE_TIME * 1000) + l1;
                    obj.addProperty("expireTime", newExpireTime);
                    // 修改过期时间
                    stringRedisTemplate.opsForValue().set(key, obj.toString(), EXPIRE_TIME, TimeUnit.SECONDS);
                }
                return true;
            }
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


}





























